package com.wine.game.wine.nettywebsocket.handler;

import cn.hutool.json.JSONNull;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.wine.game.wine.config.NettyConfig;
import com.wine.game.wine.entity.ImMessageEntity;
import com.wine.game.wine.entity.ImMessageListEntity;
import com.wine.game.wine.entity.UserEntity;
import com.wine.game.wine.nettywebsocket.NettyServer;
import com.wine.game.wine.nettywebsocket.common.MessageEnum;
import com.wine.game.wine.nettywebsocket.entity.GroupMessageEntity;
import com.wine.game.wine.nettywebsocket.entity.MessageEntity;
import com.wine.game.wine.service.ImMessageListService;
import com.wine.game.wine.service.ImMessageService;
import com.wine.game.wine.service.UserService;
import com.zenofung.common.utils.R;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.AttributeKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.Date;
import java.util.List;
import java.util.Set;

/**
 * @description: #　　　Codes are far away from bugs with the animal protecting
 * #　　　　　　　        神兽保佑,代码无bug
 * @author: zeno fung
 * @create: 2022-05-09 13:38
 */
@Component
@ChannelHandler.Sharable
public class WebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {


    @Autowired
    private ImMessageService imMessageService;
    @Autowired
    private ImMessageListService imMessageListService;
    @Autowired
    private UserService userService;

    @Autowired
    private RedisTemplate redisTemplate;


    private static final Logger log = LoggerFactory.getLogger(NettyServer.class);

    /**
     * 一旦连接，第一个被执行
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        log.info("handlerAdded 被调用" + ctx.channel().id().asLongText());
        // 添加到channelGroup 通道组
        NettyConfig.getChannelGroup().add(ctx.channel());
    }

    /**
     * 读取数据
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {

        // log.info("服务器收到消息：{}",msg.text());
        JSONObject jsonObject = JSONUtil.parseObj(msg.text());
        MessageEntity messageEntity = jsonObject.toBean(MessageEntity.class);
        if (messageEntity.getStatus().equals(MessageEnum.HEARTBEAT.getState())) {
            // log.info("心跳：{}",msg.text());
        } else if (messageEntity.getStatus().equals(MessageEnum.LOGIN.getState())) {
            // 关联channel与用户id， 广播登录状态给好友登录列表，返回好友列表未读信息，
            log.info("登录--->");
            this.login(ctx, msg, messageEntity.getContent(), MessageEnum.LOGIN.getState());
        } else if (messageEntity.getStatus().equals(MessageEnum.SENDMESSAGE_SINGLE.getState())) {
            //  单聊 如果聊天用户在线， 如果聊天用户不在线
            this.sendSingle(ctx, messageEntity.getContent());
        } else if (messageEntity.getStatus().equals(MessageEnum.LOGIN_GROUP.getState())) {
            //  加入群聊
            this.loginGroup(ctx, msg, messageEntity.getContent(), MessageEnum.LOGIN_GROUP.getState(),messageEntity.getGroupId());
        } else if (messageEntity.getStatus().equals(MessageEnum.SENDMESSAGE_GROUP.getState())) {
            // TODO 群聊
            this.sendGroup(ctx, msg, messageEntity.getContent(), MessageEnum.LOGIN_GROUP.getState(),messageEntity.getGroupId());
//            if (NettyConfig.getUserChannelMap().containsKey(messageEntity.getHid())){
//                Channel channel = NettyConfig.getUserChannelMap().get(messageEntity.getHid());
//                channel.writeAndFlush(new TextWebSocketFrame(messageEntity.getContent()));
//            }
        }


    }


    private void sendSingle(ChannelHandlerContext ctx, String content) {
        ImMessageEntity imMessageEntity = JSONUtil.toBean(content, ImMessageEntity.class);

        //判断对方是否有好友列表，没有新增， 后面再考虑不接收消息   判断自己有没有列表
        List<ImMessageListEntity> list = imMessageListService.list(new QueryWrapper<ImMessageListEntity>().eq("user_id", imMessageEntity.getUserId()).eq("friend_id", imMessageEntity.getTargetId()).or().eq("user_id", imMessageEntity.getTargetId()).eq("friend_id", imMessageEntity.getUserId()));
        ImMessageListEntity imMessageListEntity;
        if (list.size() <= 0) {
            imMessageListEntity = new ImMessageListEntity();
            imMessageListEntity.setUserId(imMessageEntity.getUserId());
            imMessageListEntity.setFriendId(imMessageEntity.getTargetId());
            imMessageListEntity.setOnLine(1);
            imMessageListService.save(imMessageListEntity);
        } else {
            imMessageListEntity = list.get(0);
        }
        if (NettyConfig.getUserChannelMap().containsKey(imMessageEntity.getTargetId())) {
            //在线直接发送并同步数据库
            Channel channel = NettyConfig.getUserChannelMap().get(imMessageEntity.getTargetId());
            imMessageEntity.setImMagListId(imMessageListEntity.getId());
            imMessageService.save(imMessageEntity);
            imMessageEntity.setMessageStatus(1);
            imMessageEntity.setMessageUser(1);
            channel.writeAndFlush(new TextWebSocketFrame(R.getJsonR(R.ok(MessageEnum.SENDMESSAGE_SINGLE.getState()).put("content", imMessageEntity))));
        } else {
            //不在线同步数据库即可
            imMessageEntity.setImMagListId(imMessageListEntity.getId());
            imMessageService.save(imMessageEntity);
        }
    }


    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        log.info("handlerRemoved 被调用" + ctx.channel().id().asLongText());
        // TODO  删除通道   下线通知，广播给好友
        NettyConfig.getChannelGroup().remove(ctx.channel());
        removeUserId(ctx);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.info("异常：{}", cause.getMessage());
        // 删除通道
        NettyConfig.getChannelGroup().remove(ctx.channel());
        removeUserId(ctx);
        ctx.close();
    }

    /**
     * 删除用户与channel的对应关系
     *
     * @param ctx
     */
    private void removeUserId(ChannelHandlerContext ctx) {
        AttributeKey<String> key = AttributeKey.valueOf("userId");
        String userId = ctx.channel().attr(key).get();
        this.quit(ctx, new TextWebSocketFrame(), "{\"id\":\"" + userId + "\",\"content\":\"quit\"}", MessageEnum.QUIT.getState());
        NettyConfig.getUserChannelMap().remove(userId);
    }


    private void login(ChannelHandlerContext ctx, TextWebSocketFrame msg, String jsonObject, String loginStatus) {
        // 获取用户ID
        UserEntity userEntity = JSONUtil.toBean(jsonObject, UserEntity.class);
        if (!StringUtils.isEmpty(userEntity)) {
            // 将用户ID作为自定义属性加入到channel中，方便随时channel中获取用户ID
            AttributeKey<String> key = AttributeKey.valueOf("userId");
            ctx.channel().attr(key).setIfAbsent(userEntity.getId());
            // 关联channel
            NettyConfig.getUserChannelMap().put(userEntity.getId(), ctx.channel());
            // 回复消息
            List<ImMessageListEntity> friend = imMessageListService.list(new QueryWrapper<ImMessageListEntity>().eq("friend_id", userEntity.getId()));
            //更改登录状态
            UserEntity userEntity1 = new UserEntity();
            userEntity1.setId(userEntity.getId());
            userEntity1.setLoginStatus(Integer.parseInt(loginStatus));
            userService.updateById(userEntity1);
            UserEntity user = userService.getById(userEntity.getId());
            friend.stream().forEach(m -> {
                if (NettyConfig.getUserChannelMap().containsKey(m.getUserId())) {
                    NettyConfig.getUserChannelMap().get(m.getUserId()).writeAndFlush(new TextWebSocketFrame(R.getJsonR(R.ok(MessageEnum.LOGIN.getState()).put("loginUser", user))));
                }
            });
            List<ImMessageListEntity> friend2 = imMessageListService.list(new QueryWrapper<ImMessageListEntity>().eq("user_id", userEntity.getId()));
            friend2.stream().forEach(m -> {
                if (NettyConfig.getUserChannelMap().containsKey(m.getFriendId())) {
                    NettyConfig.getUserChannelMap().get(m.getFriendId()).writeAndFlush(new TextWebSocketFrame(R.getJsonR(R.ok(MessageEnum.LOGIN.getState()).put("loginUser", user))));
                }
            });

            ctx.channel().writeAndFlush(new TextWebSocketFrame(R.getJsonR(R.ok())));
        } else {
            ctx.channel().writeAndFlush(new TextWebSocketFrame(R.getJsonR(R.error())));
        }

    }

    private void quit(ChannelHandlerContext ctx, TextWebSocketFrame msg, String jsonObject, String loginStatus) {
        // 获取用户ID
        log.info("服务器收到消息：{}", msg.text());
        UserEntity userEntity = JSONUtil.toBean(jsonObject, UserEntity.class);
        if (!StringUtils.isEmpty(userEntity)) {
            // 回复消息
            List<ImMessageListEntity> friend = imMessageListService.list(new QueryWrapper<ImMessageListEntity>().eq("friend_id", userEntity.getId()));
            //更改登录状态
            UserEntity userEntity1 = new UserEntity();
            userEntity1.setId(userEntity.getId());
            userEntity1.setLoginStatus(Integer.parseInt(loginStatus));
            userService.updateById(userEntity1);
            UserEntity user = userService.getById(userEntity.getId());
            friend.stream().forEach(m -> {
                if (NettyConfig.getUserChannelMap().containsKey(m.getUserId())) {
                    NettyConfig.getUserChannelMap().get(m.getUserId()).writeAndFlush(new TextWebSocketFrame(R.getJsonR(R.ok(MessageEnum.LOGIN.getState()).put("loginUser", user))));
                }
            });
            List<ImMessageListEntity> friend2 = imMessageListService.list(new QueryWrapper<ImMessageListEntity>().eq("user_id", userEntity.getId()));
            friend2.stream().forEach(m -> {
                if (NettyConfig.getUserChannelMap().containsKey(m.getFriendId())) {
                    NettyConfig.getUserChannelMap().get(m.getFriendId()).writeAndFlush(new TextWebSocketFrame(R.getJsonR(R.ok(MessageEnum.LOGIN.getState()).put("loginUser", user))));
                }
            });

            ctx.channel().writeAndFlush(new TextWebSocketFrame(R.getJsonR(R.ok())));
        } else {
            ctx.channel().writeAndFlush(new TextWebSocketFrame(R.getJsonR(R.error())));
        }

    }

    private void loginGroup(ChannelHandlerContext ctx, TextWebSocketFrame msg, String content, String state, String groupId) {
        // 获取用户ID
        UserEntity userEntity = JSONUtil.toBean(content, UserEntity.class);
        if (!StringUtils.isEmpty(userEntity)) {
            NettyConfig.addUserToGroup(groupId, userEntity.getId());
            ctx.channel().writeAndFlush(new TextWebSocketFrame(R.getJsonR(R.ok())));
            //发送群聊消息 已登录
            this.sendGroup(ctx, msg, userEntity.getId()+"已加入群聊"+groupId, MessageEnum.LOGIN_GROUP.getState(), groupId);


        } else {
            ctx.channel().writeAndFlush(new TextWebSocketFrame(R.getJsonR(R.error())));
        }
    }

    private void sendGroup(ChannelHandlerContext ctx, TextWebSocketFrame msg, String content, String state, String groupId) {
        //适合存储大规模的群聊消息，并能高效按时间范围查询，适用于需要高效时间排序和查询的场景。
//        redisTemplate.opsForZSet().add();
        // 获取用户ID
            GroupMessageEntity groupMessageEntity = new GroupMessageEntity();
            groupMessageEntity.setMessage(content);
            groupMessageEntity.setGroupId(groupId);
            groupMessageEntity.setCreateTime(new Date());
            groupMessageEntity.setStatus(1);
            // 使用 opsForZSet 获取 ZSet 操作对象
            ZSetOperations<String, String> zSetOps = redisTemplate.opsForZSet();
            zSetOps.add(NettyConfig.MESSAGE_GROUP_KEY+groupId, JSONUtil.parse(groupMessageEntity).toString(),System.currentTimeMillis());
            NettyConfig.broadcastToGroup(groupId, content);

    }
}